package lar.system.opengl;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.Vector;

import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;

import lar.system.engine.MainProgram;
import lar.system.engine.Pair;
import lar.system.engine.TouchListener;
import lar.system.engine.UI;
import lar.system.opengl.GLImageList.onSelectHandler;
import lar.system.R;
import android.drm.DrmStore.Action;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.view.MotionEvent;

public class GLStringList implements Drawable
{
	private int fontResourceId;
	private float[] glPos;
	private float[] glDim;
	private float fontHeight;
	private Vector<GLString> elements;
	private float distance = 0.03f;
	private onSelectHandler selHandler;
	private float scrollValue;
	private GLString selected;
	private boolean canSelect;
	private boolean canScroll;
	private GLStringList chain;
	private boolean chain_rec=false; //recursion protection
	private TouchListener tl; 
	
	public GLStringList(int fontResourceId, float[] glPos, float[] glDim, float fontHeight)
	{
		selected = null;
		this.glPos = glPos;
		this.glDim = glDim;
		this.fontHeight = fontHeight;
		this.fontResourceId = fontResourceId;
		tl = new GLListListener(this);
		MainProgram.glSurfaceView.addTouchListener(tl, this);
		elements = new Vector<GLString>();
		scrollValue = 0.0f;
		canSelect = true;
		canScroll = true;
		selHandler = null;
		chain = null;
	}
	
	public void finish()
	{
		MainProgram.glSurfaceView.removeTouchListener(tl);
	}
	
	public void setIsScrollable(boolean flag)
	{
		canScroll = flag;
	}
	
	public void setChainList(GLStringList list)
	{
		chain = list;
	}
	
	public GLStringList getChainList()
	{
		return chain;
	}
	
	public GLString getCoreElement(int idx)
	{
		if(idx >= elements.size() || idx < 0) return null;
		return elements.elementAt(idx);
	}
	
	public void setSelected(String str)
	{
		UI.needRendering = true;
		for(GLString i : elements) 
			if(i.getAsciiString().equals(str)) 
			{
				selected = i;
				return;
			}
		if(str == null) selected = null;
	}
	
	public int size()
	{
		return elements.size();
	}
	
	public int getSelectedIndex()
	{
		if(selected == null) return -1;
		for(int i = 0; i < elements.size(); i++) if(elements.elementAt(i) == selected) return i;
		return -1;
	}
	
	public void setSelectedIndex(int index)
	{
		UI.needRendering = true;
		if(index < 0 || index >= elements.size()) return;
		selected = elements.elementAt(index);
		if(chain != null) 
		{
			if(chain_rec)
			{
				chain_rec = false;
				return;
			}
			else
			{
				chain_rec = true;
				chain.setSelectedIndex(index);
				chain_rec = false;
			}	
		}
	}
	public void addElement(String str)
	{
		UI.needRendering = true;
		float coords[] = new float[]{
				glPos[0]-glDim[0]+fontHeight, 
				glPos[1]+glDim[1]-(fontHeight+distance)*((float)elements.size())-fontHeight/2-distance};
		elements.add(new GLString(fontResourceId, str, coords, fontHeight));
	}
	
	public void removeElement(String str)
	{
		UI.needRendering = true;
		Vector<GLString> newElements = new Vector<GLString>();
		boolean found = false;
		for(int i = 0; i < elements.size(); i++)
		{
			GLString s = elements.elementAt(i);
			if((!found && !s.getAsciiString().equals(str)) || found)
			{
				if(!found) newElements.add(s);
				else
				{
					float coords[] = new float[]{
							glPos[0]-glDim[0]+fontHeight, 
							glPos[1]+glDim[1]-(fontHeight+distance)*((float)newElements.size())-fontHeight/2-distance};
					newElements.add(new GLString(fontResourceId, s.getAsciiString(), coords, fontHeight));
				}
			}
			else 
			{
				if(selected == s) selected = null;
				found = true;
			}
		}
		elements = newElements;
	}
	
	public void removeElement(int index)
	{
		UI.needRendering = true;
		Vector<GLString> newElements = new Vector<GLString>();
		boolean found = false;
		for(int i = 0; i < elements.size(); i++)
		{
			GLString s = elements.elementAt(i);
			if((!found && index != i) || found)
			{
				if(!found) newElements.add(s);
				else
				{
					float coords[] = new float[]{
							glPos[0]-glDim[0]+fontHeight, 
							glPos[1]+glDim[1]-(fontHeight+distance)*((float)newElements.size())-fontHeight/2-distance};
					newElements.add(new GLString(fontResourceId, s.getAsciiString(), coords, fontHeight));
				}
			}
			else 
			{
				if(selected == s) selected = null;
				found = true;
			}
		}
		elements = newElements;
	}
	
	public synchronized void Draw(GL11 gl)
	{
		gl.glEnable(GL10.GL_SCISSOR_TEST);
		int[] scissorCoords = GLRenderer.getScissorCoords(glPos, glDim);
		gl.glScissor(scissorCoords[0],scissorCoords[1],scissorCoords[2],scissorCoords[3]);
		gl.glBindTexture(GL11.GL_TEXTURE_2D, 0);
		gl.glEnableClientState(GL11.GL_VERTEX_ARRAY);
		gl.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
		gl.glFrontFace(GL11.GL_CW);
		gl.glVertexPointer(3, GL11.GL_FLOAT, 0, GLEmptyQuad.vertexBuffer);
		gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, GLEmptyQuad.textureBuffer);
		gl.glPushMatrix(); 
		gl.glTranslatef(glPos[0], glPos[1], -1.0f);
		gl.glScalef(glDim[0], glDim[1], 1.0f);
		gl.glColor4f(0.1f, 0.1f, 0.1f, 1f);
		gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, 0, GLEmptyQuad.vertices.length / 3);
		gl.glColor4f(1f, 1f, 1f, 1f);
		gl.glPopMatrix();
		
		gl.glPushMatrix(); 
		gl.glTranslatef(0f, -scrollValue, 0f);
		gl.glDisableClientState(GL11.GL_VERTEX_ARRAY);
		gl.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
		for(GLString s : elements) 
		{
			float s_y = glPos[1]+glDim[1]-(fontHeight+distance)*((float)elements.indexOf(s))-fontHeight/2-distance;
			if(s_y > glPos[1] + glDim[1] + scrollValue + fontHeight || s_y < glPos[1] - glDim[1] + scrollValue - fontHeight) continue;
			if(selected == s)
			{
				float sel_y = glPos[1]+glDim[1]-(fontHeight+distance)*((float)elements.indexOf(selected))-fontHeight/2-distance;
				gl.glBindTexture(GL11.GL_TEXTURE_2D, 0);
				gl.glEnableClientState(GL11.GL_VERTEX_ARRAY);
				gl.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
				gl.glFrontFace(GL11.GL_CW);
				gl.glVertexPointer(3, GL11.GL_FLOAT, 0, GLEmptyQuad.vertexBuffer);
				gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, GLEmptyQuad.textureBuffer);
				gl.glPushMatrix(); 
				gl.glTranslatef(glPos[0], sel_y, -1.0f);
				gl.glScalef(glDim[0], (fontHeight+distance)/2, 1.0f);
				gl.glColor4f(1f, 0.5f, 0f, 1f);
				gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, 0, GLEmptyQuad.vertices.length / 3);
				gl.glColor4f(1f, 1f, 1f, 1f);
				gl.glPopMatrix();
				gl.glDisableClientState(GL11.GL_VERTEX_ARRAY);
				gl.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
			}
			s.Draw(gl);
		}
		
		gl.glPopMatrix();
		gl.glDisableClientState(GL11.GL_VERTEX_ARRAY);
		gl.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
		gl.glDisable(GL10.GL_SCISSOR_TEST);
		
	}

	public void addScroll(float value)
	{
		UI.needRendering = true;
		if(chain != null) 
		{
			if(chain_rec) //ring-loop 
			{
				chain_rec = false;
				return; //we cut the ring here
			}
			else 
			{
				chain_rec = true;
				chain.addScroll(value);
				chain_rec = false;
			}
		}
		if(!canScroll) return;
		float elem_count = (glDim[1]*2 - distance)/(fontHeight+distance);
		if(elem_count > elements.size()) return;
		if(scrollValue+value < -((elements.size()-(int)elem_count)*(fontHeight+distance))) return;
		else if(scrollValue+value > 0) return;
		scrollValue += value;
		
	}
	
	
	/*
	 * 
		if(scrollValue+value < -((elements.size()-(int)elem_count)*(fontHeight+distance))) scrollValue = -((elements.size()-(int)elem_count)*(fontHeight+distance));
		else if(scrollValue+value > 0) scrollValue = 0;
		else scrollValue += value;
	 */
	public float getScroll()
	{
		return scrollValue;
	}
	
	public void setIsSelectible(boolean flag)
	{
		canSelect = flag;
	}
	
	public boolean CheckBounds(float x, float y) 
	{
		if(x > glPos[0]+glDim[0] || x < glPos[0]-glDim[0]) return false;
		if(y > glPos[1]+glDim[1] || y < glPos[1]-glDim[1]) return false;
		return true;
	}
	
	public synchronized void onSelect(float y)
	{
		if(!canSelect) return;
		float start_y = glPos[1]+glDim[1];
		float end_y = glPos[1]-glDim[1];
		y = 1f - (y / MainProgram.glSurfaceView.getHeight())*2f;
		float a = (y-start_y+scrollValue)/(end_y-start_y);
		float elem_count = (glDim[1]*2 - distance)/(fontHeight+distance);
		int idx = (int)(a*elem_count);
		if(idx >= 0 && idx < elements.size()) 
		{
			selected = elements.elementAt(idx);
			if(chain != null) chain.setSelectedIndex(idx);
			if(selHandler != null) selHandler.onSelect(idx, y);
		}
		
	}
	
	public Pair<Integer, Integer> getVisibleBounds()
	{
		float elem_count = (glDim[1]*2 - distance)/(fontHeight+distance);
		float singleElemSize = glDim[1]*2 / elem_count;
		float firstSeen = -this.scrollValue / singleElemSize;
		//Log.d("R", "Low: " + firstSeen + "High: " + (firstSeen+elem_count));
		return new Pair<Integer, Integer>((int)firstSeen, (int)(firstSeen+elem_count));
	}
	
	public int getNumberElementsFit()
	{
		float elem_count = (glDim[1]*2 - distance)/(fontHeight+distance);
		return (int)elem_count;
	}
	
	public String getSelected()
	{
		return selected == null ? null : selected.getAsciiString();
	}
	
	public void setOnSelectHandler(onSelectHandler handler)
	{
		this.selHandler = handler;
	}
	
	//**********************************************************
	public interface onSelectHandler
	{
		public void onSelect(int idx, float y);
	}
		
	//**********************************************************
		

	class GLListListener implements TouchListener
	{
		private GLStringList list;
		private float last_y;
		private float start_y, end_y;
		boolean drag;
		
		public GLListListener(GLStringList list)
		{
			this.list = list;
			last_y = 0.0f;
			
		}
		public void onTouchEvent(MotionEvent e) 
		{
			if(e.getAction() == MotionEvent.ACTION_MOVE)
			{
				float y = e.getY();
				
				if(Math.abs((y-start_y)/MainProgram.glSurfaceView.getHeight()) > 0.01) 
				{
					drag = true;
					list.addScroll(2*(y-last_y)/MainProgram.glSurfaceView.getHeight());
				}
				last_y = y;
			}
			else if(e.getAction() == MotionEvent.ACTION_DOWN)
			{
				last_y = e.getY();
				start_y = last_y;
				drag = false;
			}
			else if(e.getAction() == MotionEvent.ACTION_UP)
			{
				end_y = e.getY();
				if(!drag) list.onSelect(end_y);
			}
			
		}
	}

}

